/*
 * HexGame.cpp
 *
 *  Created on: Apr 5, 2014
 *      Author: zby
 */
#include "Board.h"
#include "HexGame.h"
#include <limits>
#include <vector>
#include <algorithm>
#include <ctime>        // std::time
#include <cstdlib>      // std::rand, std::srand
#include <map>

using graph::Node;
using graph::NodeVal;
using board::_eplayer;
using board::PLAYER_A;
using board::PLAYER_B;
using board::NONE;
using board::pos_X;
using board::pos_Y;

HexGame::HexGame() : MonteCarloLimit(1000)
{
    std::srand ( unsigned ( std::time(0) ) );
    pBoardGame = NULL;
    bExit=false;

}

HexGame::~HexGame() {
    if(NULL!=pBoardGame) delete pBoardGame;
    pBoardGame=NULL;
    bExit=false;
}

/*
* @brief    At the very beginning of start. Defin the size of board and player's color ('x' or 'o').
*             'x' always starts first.
* @param    none
* @retval    none
*/
void HexGame::usermenu(void)
{
    cout<<"**********************************************************************************************"<<endl;
    cout<<"*                              (ascii) HEX-GAME v.2                                          *"<<endl;
    cout<<"*                                                                                            *"<<endl;
    cout<<"* -------------------------------------------------------------------------------------------*"<<endl;
    cout<<"* HalSoft                       c++ training purpose.                                        *"<<endl;
    //cout<<"* source: https://code.google.com/p/hex-game-zh/source/browse/#svn%2Ftrunk%2FHexGame%2Fsrc *"<<endl;
    cout<<"**********************************************************************************************"<<endl;
    cout<<endl<<endl<<endl;
    unsigned int user_size;

    while(1){

        cout<<"-Put size of board(max 100):"<<endl;
        cin>>user_size;
        if(cin.good()) {
            cin.clear();
            cin.ignore( std::numeric_limits < std::streamsize >::max(), '\n' );//std::cin.sync();
            break;
        }
        else cout<<"invalid parameter"<<endl;
        cin.clear();
        cin.ignore( std::numeric_limits < std::streamsize >::max(), '\n' );//std::cin.sync();
    }
    pBoardGame= new Board(user_size);
    if(pBoardGame == NULL) throw "memory limit";


    string input;
    while(1){
        cout<<"-Choose 'x' or 'o'"<<endl;
        getline(cin, input);
        if (input.length() == 1 &&  'x'==input[0] ) {
             this->isUserPlayerA=true;
             break;
           }
        else if(input.length() == 1 && 'o'==input[0] ){
             this->isUserPlayerA=false;
             break;
        }
    }
}

/*
* @brief    Take users' move. Format: x,y is required. Press 'q' for exit game.
*             After usage this method check first if user has pressed 'q'(bExit==true).
*             If not method return user move otherwise method return always 0.
* @param    none
* @retval    user move
*/
Node HexGame::yourMove(void)
{
    pos_X x;
    pos_Y y;
    _eplayer eplayer;
    string input;
    string nr1, nr2;

    while(1){
        cout<<"-Put your move (x,y):";
        getline(cin, input);

        if(string::npos!=input.find("q")){
            this->bExit=true;
            return static_cast<Node>(0);
        }
        else if(string::npos==input.find_first_not_of("0123456789, ") && input.find_first_of(",")==input.find_last_of(",") &&
           string::npos!=input.find_first_of(",")) {
            nr1 = input.substr(0,input.find_first_of(","));
            nr2 = input.substr(input.find_first_of(",")+1,input.length());
            x=static_cast<pos_X>(atoi(nr1.c_str()));
            y=static_cast<pos_Y>(atoi(nr2.c_str()));

            if(NONE==this->pBoardGame->Get_node_value(static_cast<Node>(x*this->pBoardGame->GetBoardSize()+y))) break;
            else cout<<"this field is busy"<<endl;
        }
        else cout<<"invalid parameter"<<endl;
    }
    cout<<"("<<x<<","<<y<<")"<<endl;

    if(this->isUserPlayerA) eplayer = PLAYER_A;
    else eplayer = PLAYER_B;
    this->pBoardGame->put_move(x,y,eplayer);
    return (static_cast<Node>(x*this->pBoardGame->GetBoardSize()+y));
}

/*
* @brief    Generate the computer moves. Each move is estimated on the base of Monter-Carlo algorithm.
* @param    none
* @retval    best move
*/
Node HexGame::compMove(void)
{
    //find vacant fields
    vector<Node> vVacancy, vVacancy_shuffle;
    for(unsigned int i=0; i<(this->pBoardGame->GetBoardSize()*this->pBoardGame->GetBoardSize()); i++)
        if( NONE==static_cast<_eplayer>(this->pBoardGame->Get_node_value((Node)i))) vVacancy.push_back(i);
    vVacancy_shuffle=vVacancy;
    //copy current state of board
    Board *pBoardCpy1, *pBoardCpy2;
    //variable for estimate how good move is good.
    int ctr_win=0;
    int ctr_sw=0;
    std::map<float,Node, greater<float> > best_move;

    for( vector<Node>::const_iterator cit1=vVacancy.begin(); cit1!=vVacancy.end(); ++cit1 ){

        //restore board
        pBoardCpy1= new Board(*pBoardGame);
        //put one move to investigate how good is
        if(this->isUserPlayerA)
            pBoardCpy1->put_move( (pos_X)(*cit1/pBoardCpy1->GetBoardSize()),
                    (pos_Y)(*cit1%pBoardCpy1->GetBoardSize()), (_eplayer)PLAYER_B);
        else
            pBoardCpy1->put_move( (pos_X)(*cit1/pBoardCpy1->GetBoardSize()),
                    (pos_Y)(*cit1%pBoardCpy1->GetBoardSize()), (_eplayer)PLAYER_A);

        //check how good the move is by fill rest of vacant fields randomly
        //moves have to be put alternately - user then comp
        //starting form user due to comps' move was put previously
        for(int i=0; i<this->MonteCarloLimit; i++)
        {
            //back to the square one
            pBoardCpy2=new Board(*pBoardCpy1);
            //make possible move as random
            std::random_shuffle(vVacancy_shuffle.begin(), vVacancy_shuffle.end());
            for(vector<Node>::const_iterator cit2=vVacancy_shuffle.begin() ; cit2!=vVacancy_shuffle.end(); ++cit2 )
            {
                //do not put the same move which we investigate(is not vacant)
                if(*cit1==*cit2) continue;

                if(ctr_sw%2){
                    //comps' move
                    if(this->isUserPlayerA)
                        pBoardCpy2->put_move( (pos_X)(*cit2/pBoardCpy2->GetBoardSize()),
                                (pos_Y)(*cit2%pBoardCpy2->GetBoardSize()), (_eplayer)PLAYER_B);
                    else
                        pBoardCpy2->put_move( (pos_X)(*cit2/pBoardCpy2->GetBoardSize()),
                                (pos_Y)(*cit2%pBoardCpy2->GetBoardSize()), (_eplayer)PLAYER_A);
                }
                else{
                    //users' move
                    if(this->isUserPlayerA)
                        pBoardCpy2->put_move( (pos_X)(*cit2/pBoardCpy2->GetBoardSize()),
                                (pos_Y)(*cit2%pBoardCpy2->GetBoardSize()), (_eplayer)PLAYER_A);
                    else
                        pBoardCpy2->put_move( (pos_X)(*cit2/pBoardCpy2->GetBoardSize()),
                            (pos_Y)(*cit2%pBoardCpy2->GetBoardSize()), (_eplayer)PLAYER_B);
                }
                ctr_sw++;
            }

            //check who won. If comp won increment cmop's win
            if(true==this->isUserPlayerA && true==pBoardCpy2->fau_SN.ifTheSameRoot(0,(pBoardCpy2->GetBoardSize()* pBoardCpy2->GetBoardSize())-1))
                ctr_win++;
            else if(false==this->isUserPlayerA && true==pBoardCpy2->fau_WE.ifTheSameRoot(0,(pBoardCpy2->GetBoardSize()* pBoardCpy2->GetBoardSize())-1))
                ctr_win++;

            delete pBoardCpy2;
        }
        best_move.insert(std::pair<float, Node>(static_cast<float>(((float)ctr_win)/this->MonteCarloLimit),*cit1));

        ctr_win=0;
        ctr_sw=0;
        delete pBoardCpy1;
    }

    //get the best move
    Node BestMove= best_move.begin()->second;
    if(this->isUserPlayerA)
        this->pBoardGame->put_move((pos_X)(BestMove/this->MonteCarloLimit),(pos_Y)(BestMove%this->MonteCarloLimit), (_eplayer)PLAYER_B);
    else
        this->pBoardGame->put_move((pos_X)(BestMove/this->MonteCarloLimit),(pos_Y)(BestMove%this->MonteCarloLimit), (_eplayer)PLAYER_A);

    return BestMove;
}

/*
* @brief    Decide who won (one the base of FindAndUnion algorithm).
*             If the root of West edge is the same as East edge won playerA
*             If the root of North edge is the same as South edge won playerB.
*             If not both return NONE - means there is not winner yet.
* @param    none
* @retval    PLAYER_A or PLAYER_B or NONE if there is no winer
*/
_eplayer HexGame::whoWon(void)
{
    if(this->pBoardGame->fau_WE.ifTheSameRoot(0, (this->pBoardGame->GetBoardSize()*this->pBoardGame->GetBoardSize())-1)) return PLAYER_A;
    else if(this->pBoardGame->fau_SN.ifTheSameRoot(0, (this->pBoardGame->GetBoardSize()*this->pBoardGame->GetBoardSize())-1)) return PLAYER_B;
    else return NONE;
}

/*
* @brief    Starts hex game. Press 'q' for exit.
* @param    none
* @retval    none
*/
void HexGame::play(void)
{
    Node move;
    pos_X x;
    pos_Y y;

    this->usermenu();
    cout<<"=================>> info: press 'q' to exit <<================="<<endl;

    cout<<*this->pBoardGame;
    cout<<endl<<endl<<endl;

    cout<<"----------------->> start--------------------------------------"<<endl;

    while(!this->bExit){

        if(this->isUserPlayerA){
            move=this->yourMove();
            x= static_cast<Node>(move/this->pBoardGame->GetBoardSize());
            y= static_cast<Node>(move%this->pBoardGame->GetBoardSize());
            this->pBoardGame->put_move(x,y,static_cast<_eplayer>(PLAYER_A));
        }
        else{
            cout<<"thinking..."<<endl;
            move=this->compMove();
            x= static_cast<Node>(move/this->pBoardGame->GetBoardSize());
            y= static_cast<Node>(move%this->pBoardGame->GetBoardSize());
            this->pBoardGame->put_move(x,y,static_cast<_eplayer>(PLAYER_A));
        }
        cout<<*this->pBoardGame;
        //who won
        if( (true==this->isUserPlayerA && PLAYER_A==this->whoWon()) || (false==this->isUserPlayerA && PLAYER_B==this->whoWon()) )
        {
            cout<<"-->You won!!"<<endl;
            break;
        }
        if( (true==this->isUserPlayerA && PLAYER_B==this->whoWon()) || (false==this->isUserPlayerA && PLAYER_A==this->whoWon()) )
        {
            cout<<"-->You lost."<<endl;
            break;
        }

        if(this->isUserPlayerA){
            cout<<"thinking..."<<endl;
            move=this->compMove();
            x= static_cast<Node>(move/this->pBoardGame->GetBoardSize());
            y= static_cast<Node>(move%this->pBoardGame->GetBoardSize());
            this->pBoardGame->put_move(x,y,static_cast<_eplayer>(PLAYER_B));
        }
        else{
            move=this->yourMove();
            x= static_cast<Node>(move/this->pBoardGame->GetBoardSize());
            y= static_cast<Node>(move%this->pBoardGame->GetBoardSize());
            this->pBoardGame->put_move(x,y,static_cast<_eplayer>(PLAYER_B));
        }
        cout<<*this->pBoardGame;

        //who won
        if( (true==this->isUserPlayerA && PLAYER_A==this->whoWon()) || (false==this->isUserPlayerA && PLAYER_B==this->whoWon()) )
        {
            cout<<"-->You won!!"<<endl;
            break;
        }
        if( (true==this->isUserPlayerA && PLAYER_B==this->whoWon()) || (false==this->isUserPlayerA && PLAYER_A==this->whoWon()) )
        {
            cout<<"-->You lost."<<endl;
            break;
        }
    }

    cout<<"Game stopped"<<endl<<endl;
    cout<<*pBoardGame<<endl;
    cout<<"---------------------------------"<<endl;
    cout<<"want to code? "<<"see source:"<<endl<<"https://code.google.com/p/hex-game-zh/source/browse/#svn%2Ftrunk%2FHexGame%2Fsrc"<<endl;
    int exit;
    cin>>exit;
}














